package org.piwicode.bywordofmouth;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.io.File;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author pierre
 */
public class ShareWorker {

    private static final Logger LOG = Logger.getLogger(ShareWorker.class.getName());
    private final Share share;
    private final ExecutorService service = Executors.newSingleThreadExecutor();
    private final AtomicReference<FutureTask<Void>> task = new AtomicReference<>();

    public ShareWorker(Share share) {
        this.share = Preconditions.checkNotNull(share);
    }

    FutureTask<Void> changeDirectories(final ImmutableList<Directory> directories) {
        LOG.log(Level.INFO, "Change the shared directories list");
        share.directories.set(directories);
        LOG.log(Level.INFO, "Request resource scan");
        return scanResources();
    }

    FutureTask<Void> scanResources() {
        final FutureTask<Void> newTask = new FutureTask<>(new ScanResources());
        service.execute(newTask);
        final FutureTask<Void> oldTask = task.getAndSet(newTask);
        if (oldTask != null) {
            oldTask.cancel(true);
        }
        return newTask;
    }

    class ScanResources implements Callable<Void> {

        @Override
        public Void call() throws InterruptedException, NoSuchAlgorithmException {
            LOG.log(Level.INFO, "Start resource scan");
            //Scan resources
            final ImmutableList.Builder<Resource> resourcesBldr = ImmutableList.builder();
            final ImmutableSet.Builder<File> fileBldr = ImmutableSet.builder();
            final ImmutableList<Directory> directories = share.directories.get();
            for (final Directory directory : directories) {
                try {
                    directory.processFiles(share.rootResource(), resourcesBldr, fileBldr);
                } catch (IOException ex) {
                    LOG.
                            log(Level.SEVERE, "Unexpected failure while scanning directory {0}. The directory view may appear incomplete. Please report.", directory);
                }
            }
            final ImmutableSet<File> files = fileBldr.build();
            final ImmutableList<Resource> resources = resourcesBldr.build();
            share.resources.set(resources);

            // Clean the hash store
            LOG.log(Level.INFO, "Cleanning the hash store");
            share.fileHash.keySet().retainAll(files);
            {
                final Iterator<Map.Entry<Hash, Chunk>> itr = share.hashChunk.entrySet().iterator();
                while (itr.hasNext()) {
                    if (!files.contains(itr.next().getValue().file())) {
                        itr.remove();
                    }
                }
            }
            //Feed the hash store
            LOG.log(Level.INFO, "Feeding hashing files");
            final Hasher hasher = new Hasher();
            for (final File file : files) {
                if (share.fileHash.containsKey(file)) {
                    continue;
                }
                try {
                    share.fileHash.put(file, hasher.hash(file));
                } catch (IOException ex) {
                    LOG.log(Level.SEVERE, "Failed to hash file", ex);
                }
            }
            LOG.log(Level.INFO, "Hashing {0} resource completed", files.size());
            return null;
        }
    }
}
